package com.mkx.core.wd;

import com.mkx.core.WatchDog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class ThreadPoolWatchDog implements WatchDog {
    
    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolWatchDog.class);
    
    private final ScheduledExecutorService scheduler;
    private final RedisTemplate<String, Object> redisTemplate;
    private final String lockKey;
    private final String lockValue;
    private final long renewInterval;
    private ScheduledFuture<?> renewTask;
    private volatile boolean running = false;
    
    private static final String RENEW_SCRIPT = """
        -- 增加对nil值的额外检查，提高集群环境下的鲁棒性
        -- 使用Lua脚本确保续期操作的原子性
        local currentValue = redis.call('get', KEYS[1])
        if currentValue == ARGV[1] then
            -- 锁归当前客户端所有，可以续期
            return redis.call('pexpire', KEYS[1], ARGV[2])
        elseif currentValue == nil then
            -- 锁已不存在，返回0表示续期失败
            return 0
        else
            -- 锁被其他客户端持有，返回0表示续期失败
            return 0
        end
        """;
    
    public ThreadPoolWatchDog(RedisTemplate<String, Object> redisTemplate, 
                             String lockKey, 
                             String lockValue, 
                             long renewInterval) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
        this.lockValue = lockValue;
        this.renewInterval = renewInterval;
        
        // 使用虚拟线程
        this.scheduler = Executors.newScheduledThreadPool(1, 
            Thread.ofVirtual().name("redis-watchdog-threadpool-", 0).factory());
    }
    
    @Override
    public void start() {
        if (!running) {
            renewTask = scheduler.scheduleAtFixedRate(
                this::renewLock, 
                renewInterval / 3,
                renewInterval, 
                TimeUnit.MILLISECONDS
            );
            running = true;
            logger.debug("ThreadPool WatchDog started for lock: {}", lockKey);
        }
    }
    
    @Override
    public void stop() {
        if (running) {
            if (renewTask != null) {
                renewTask.cancel(true);
                renewTask = null;
            }
            scheduler.close();
            running = false;
            logger.debug("ThreadPool WatchDog stopped for lock: {}", lockKey);
        }
    }
    
    @Override
    public boolean isRunning() {
        return running;
    }
    
    private void renewLock() {
        try {
            var script = new DefaultRedisScript<Long>(RENEW_SCRIPT, Long.class);
            
            Long result = redisTemplate.execute(script, 
                Collections.singletonList(lockKey), 
                lockValue, 
                String.valueOf(renewInterval * 2));
            
            if (result != null && result == 1) {
                logger.trace("Lock renewed by ThreadPool WatchDog: {}", lockKey);
            } else {
                logger.warn("Lock renewal failed by ThreadPool WatchDog, maybe released: {}", lockKey);
                this.stop();
            }
        } catch (Exception e) {
            logger.error("Error renewing lock by ThreadPool WatchDog: {}", lockKey, e);
            this.stop();
        }
    }
}